package cc.lj.internal.coupons.domain.ticket.service.impl.useTicketRule;

import cc.lj.internal.api.ordershare.dto.OrderOnlineAPIDTO;
import cc.lj.internal.core.exception.ServiceException;
import cc.lj.internal.coupons.domain.ticket.model.vo.TicketBatchDO;
import cc.lj.internal.coupons.domain.ticket.model.vo.TicketMainDO;
import cc.lj.internal.coupons.domain.ticket.repository.IUseTicketCheckCacheRepository;
import cc.lj.internal.coupons.domain.ticket.service.AbstractUseTicketRuleDomainService;
import cc.lj.internal.coupons.infrastructure.excepion.CoupousExceptionEnum;
import cc.lj.internal.coupons.infrastructure.po.BatchBindRelationPO;
import cc.lj.internal.coupons.infrastructure.repository.BatchBindRelationRepository;
import cc.lj.internal.coupons.interfaces.dto.command.UseTicketCheckCommand;
import lj.internal.dict.enums.ticketBatch.EnumTicketSingleStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 用券验证规则 - 券独立使用规则
 * @author liangxifeng
 * @date 2023/6/26 15:12
 */
@Service("useTicketSingleRuleDomainServiceImpl")
@Slf4j
public class UseTicketSingleRuleDomainServiceImpl extends AbstractUseTicketRuleDomainService {
    //用券验证 redis 缓存仓储接口
    @Resource(name = "usetTicketCheckCacheRedisRepositoryImpl")
    private IUseTicketCheckCacheRepository cacheRepository;

    //券批次绑定关系表仓储接口
    @Resource(name = "batchBindRelationRepository")
    private BatchBindRelationRepository bindRelationRepository;

    @Override
    protected void rule(TicketBatchDO batchDO, TicketMainDO ticketMainDO, UseTicketCheckCommand ticketCheckCommand, OrderOnlineAPIDTO orderOnlineAPIDTO) {
        log.info("【电子券用券验证】：独立使用规则验证开始==");
        if(ticketCheckCommand.getUuId() == null) throw new ServiceException(CoupousExceptionEnum.USE_TICKET_CHECK_UUID_IS_NULL);

        /**
         * 从redis 中读取 已用券数据
         */
        //1.从redis中读取已用券列表和券批次使用数量 <券使用码，批次主键>
        //Map<String,Integer> usedTicketHash = cacheRepository.getUseTicketList(ticketCheckCommand.getPactNum(),ticketCheckCommand.getUuId());
        //2.从redis中读取券批次使用数量 <券批次主键，已用券数量>
        Map<Integer,Integer> usedBatchNumHash = cacheRepository.getUseBatch(ticketCheckCommand.getPactNum(),ticketCheckCommand.getUuId());
        //3.从redis中读取券用券其他数据 Map<single_batch_id: 独立使用的券批次主键, limit_value: 满减剩余额度空间>
        Map<String,Object> usedOther = cacheRepository.getUseTicketOther(ticketCheckCommand.getPactNum(),ticketCheckCommand.getUuId());

        /**
         * 验证独立使用，不同券批次验证独立使用，相同券批次不做任何处理
         * 1. 已用券列表存在独立使用券
         * 2. 当前券批次是独立使用券
         * 3. 当前券为捆绑券 (需先用主券，才能用当前券)
         */
        //1. 判断之前用过的券独立使用： redis中是否存在独立使用券批次
        if(!usedOther.isEmpty() && usedOther.get("single_batch_id") != null ) {
            //已用券列表中的 独立使用券批次主键
            int usedSingleBatchId = Integer.valueOf( usedOther.get("single_batch_id").toString() );
            //如果当前券批次主键 != 已用券列表中的 独立使用券批次主键（独立使用券，不能与其他其他批次券公用）
            if(batchDO.getBatchId() != usedSingleBatchId) {
                log.error("独立使用规则：已用券中存在独立使用券，当前券批次主键batchId:"+batchDO.getBatchId()+",已用券列表中独立使用券批次主键batchId:"+usedSingleBatchId);
                throw new ServiceException(CoupousExceptionEnum.USED_LIST_HASH_SINGLE);
            } else {
                log.info("独立使用规则：该券batchId:"+batchDO.getBatchId()+",券编码"+ticketCheckCommand.getTicketUseCode()+",redis已用券列表中有独立使用的券，当前券和独立使用的券是同一个批次,已用券列表中独立使用券批次主键batchId:"+usedSingleBatchId);
            }
        }else {
            log.info("独立使用规则：该券batchId:"+batchDO.getBatchId()+",券编码"+ticketCheckCommand.getTicketUseCode()+",redis已用券列表中没有独立使用的券");
        }

        //2.判断当前券独立使用
        if( batchDO.getSingleFlag() == EnumTicketSingleStatus.SINGLE.getCode() ) {
            if(usedBatchNumHash.size() > 0 ) { //redis中已经有用过的电子券
                if( usedBatchNumHash.size() > 1 || !usedBatchNumHash.containsKey(batchDO.getBatchId()) ){
                    log.error("独立使用规则：该券是独立用券，券批次batchId:"+batchDO.getBatchId()+",已用券批次中存在其他批次券，不可用券！已用{券批次=数量}=>"+usedBatchNumHash);
                    throw new ServiceException(CoupousExceptionEnum.USED_TICKET_CUR_SINGLE);
                }else {
                    log.info("独立使用规则：该券batchId:"+batchDO.getBatchId()+",券编码"+ticketCheckCommand.getTicketUseCode()+",为独立使用券，redis已用券列表中没有找到其他批次券，该券可用!");
                }
            }
            //设置redis中该批次为独立使用券
            Map<String,Object> redisBatchOther = new HashMap<>();
            redisBatchOther.put("single_batch_id",batchDO.getBatchId());
            cacheRepository.setUseTicketOther(ticketCheckCommand.getPactNum(),ticketCheckCommand.getUuId(),redisBatchOther);
        }
        //3. 当前券为捆绑券 (需先用主券，才能用当前券)
        if(batchDO.getSingleFlag() == EnumTicketSingleStatus.BIND.getCode()) {
            //通过副券主键查询 券批次绑定关系表 获取主券批次主键
            BatchBindRelationPO batchBindRelationPOParam = new BatchBindRelationPO();
            batchBindRelationPOParam.setChildBatchId(batchDO.getBatchId());
            List<BatchBindRelationPO> bindRelationPOList = bindRelationRepository.getList(batchBindRelationPOParam);
            if(bindRelationPOList.size() == 0) {
                log.error("独立使用规则：券批次batchId:"+batchDO.getBatchId()+",是副券，通过副券批次主键"+batchDO.getBatchId()+",在绑定关系表中没有查询到绑定数据");
                throw new ServiceException(CoupousExceptionEnum.BATCH_BIND_RELATION_LIST_NULL);
            }

            //定义是否在绑定关系中已券券批次中找到主券，找到：1, 否则：0
            AtomicInteger isFindMainBatchId = new AtomicInteger(0);
            //遍历主券，判断主券批次是否存在于 已用券批次数据中
            for (BatchBindRelationPO batchBindRelationPO : bindRelationPOList) {
                //如果绑定的主券批次主键没有在已用券批次中，则不允许继续用券
                if( usedBatchNumHash.containsKey( batchBindRelationPO.getMainBatchId() )) {
                    isFindMainBatchId.compareAndSet(0,1);
                    break;
                }
            }
            //如果在已用券批次中没有找到副券，则不用继续用券
            if( isFindMainBatchId.get()  == 0 ) {
                log.error("独立使用规则：券批次batchId:"+batchDO.getBatchId()+",是副券，且在已用券批次中没有找到主券，绑定关系:"+bindRelationPOList+",已用券批次<券批次主键，使用张数>:"+usedBatchNumHash);
                throw new ServiceException(CoupousExceptionEnum.CHILD_TICKTE_USE_ERROR);
            }else {
                log.info("独立使用规则：该券batchId:"+batchDO.getBatchId()+",券编码"+ticketCheckCommand.getTicketUseCode()+",为捆绑副券，redis已用券列表中存在主券，该券可用");
            }
        }
        log.info("【电子券用券验证】：独立使用规则验证通过==");
    }
}
